#include "StdAfx.h"
#include "MLConsoleCluStream.h"
#include "MLConsolePrinter.h"
#include "MLTools.h"

CMLConsoleCluStream::CMLConsoleCluStream(void)
{
}

CMLConsoleCluStream::~CMLConsoleCluStream(void)
{
}

int CMLConsoleCluStream::Run(const std::vector<std::wstring> &vCommand)
{
	int nRet;
	int nComIndex = 1;
	if (vCommand.size() < 14)
	{
		_ASSERT(false);
		return -1;
	}
	m_wstrInputFileName  = vCommand[nComIndex++];
	m_wstrOutputFileName = vCommand[nComIndex++];
	m_nNumMacroClusters  = _wtoi(vCommand[nComIndex++].c_str());
	m_nStreamBufferSize  = _wtoi(vCommand[nComIndex++].c_str());
	m_nStreamMaxLength  = _wtoi(vCommand[nComIndex++].c_str());
	m_nHorizonLength = _wtoi(vCommand[nComIndex++].c_str());
	m_nStatLength = _wtoi(vCommand[nComIndex++].c_str());
	m_nInitNumber = _wtoi(vCommand[nComIndex++].c_str());
	m_nInitNumMicroClusters = _wtoi(vCommand[nComIndex++].c_str());
	m_nMaxNumMicroClusters  = _wtoi(vCommand[nComIndex++].c_str());
	m_dMaxMicroClusterRadius = _wtof(vCommand[nComIndex++].c_str());
	m_dMaxTimeStampBelow = _wtof(vCommand[nComIndex++].c_str());
	m_nNumLastTimeCheck  = _wtoi(vCommand[nComIndex++].c_str());

	m_bDisplayHorzionSSQ = false;
	m_bDisplayRawFormat = false;
	m_bDisplayOnlyTime = false;
	while ((int)vCommand.size() > nComIndex)
	{
		std::wstring wszDisplay = vCommand[nComIndex++];
		if(_wcsicmp(wszDisplay.c_str(), L"displayhorizon") == 0)
			m_bDisplayHorzionSSQ = true;
		else if(_wcsicmp(wszDisplay.c_str(), L"displayrawformat") == 0)
			m_bDisplayRawFormat = true;
		else if(_wcsicmp(wszDisplay.c_str(), L"displayonlytime") == 0)
			m_bDisplayOnlyTime = true;
	}

	CMLConsolePrinter::PrintConsole(L"CluStream starts.\n");

	// Initialize the CluStream
	nRet = InitCluStream();
	ML_ON_FAILED_RETURN(nRet);

	// Initialize the data stream loader
	nRet = InitStreamLoader();
	ML_ON_FAILED_RETURN(nRet);

	// Initialize the result output file
	nRet = InitOutputFile();
	ML_ON_FAILED_RETURN(nRet);

	// Run CluStream
	nRet = RunCluStream();
	ML_ON_FAILED_RETURN(nRet);

	// Close 
	CloseAll();

	CMLConsolePrinter::PrintConsole(L"CluStream finishes.\n");
	return 0;
}

int CMLConsoleCluStream::RunCluStream(void)
{
	int nRet;
	int i;
	long nStartClock;
	long nCurrentClock;

	// Process data
	mylablib::CMLDataSet dataSet;
	mylablib::CMLDataSet horizonDataSet;
	std::vector<mylablib::CMLDataCluster> vClusters;
	std::vector<mylablib::CMLDataPoint> vCenters;
	int nDataIndex = 0;

	nStartClock = mylablib::CMLTools::GetCurrentClock();
	nRet = m_StreamLoader.Load(dataSet, m_nStreamBufferSize);
	if (ML_FAILED(nRet))
	{
		m_OutputFile.close();
		m_StreamLoader.Close();
		_ASSERT(false);
		return nRet;
	}

	CMLCluStreamSnapshot preSnap;
	m_CluStream.SaveSnapshot(preSnap);

	double dAvgSSQ = 0.0;
	double dSSQ;
	int nStatCount = 0;
	int nHorizonLen = m_nHorizonLength;
	int nHorizonIndex=0;
	horizonDataSet.Resize(nHorizonLen);
	
	while (dataSet.Count() > 0 )
	{
		int nPointIndex;
		for (nPointIndex=0; nPointIndex<dataSet.Count(); nPointIndex++)
		{
			m_CluStream.Insert(dataSet.Get(nPointIndex));
			nDataIndex++;

			if (nDataIndex < m_nInitNumber)
				continue;
			horizonDataSet.Set(nHorizonIndex, dataSet.Get(nPointIndex));
			nHorizonIndex++;
			if (nHorizonIndex == m_nHorizonLength)
			{
				nHorizonIndex = 0;
				CMLConsolePrinter::PrintConsole(L"Data Index : %d\n",nDataIndex);
				// Create macro-clusters;
				m_CluStream.CreateMacroClusters(m_nNumMacroClusters, vClusters, preSnap);
				// Collect the centers of macro-clusters
				vCenters.resize(vClusters.size());
				for (i=0; i<(int)vClusters.size(); i++)
					vCenters[i] = vClusters[i].center;

				if (m_bDisplayOnlyTime)
				{
					nCurrentClock = mylablib::CMLTools::GetCurrentClock();
					m_OutputFile << nDataIndex << L','<< nCurrentClock - nStartClock << L"\n";
				}
				else
				{
					// Calculate the SSQ
					dSSQ = mylablib::CMLTools_Stat::CalSSQ(horizonDataSet, vCenters, m_CluStream.GetDistanceFunc());
					dAvgSSQ += dSSQ;

					if (m_bDisplayHorzionSSQ)
					{
						// Output the SSQ
						nCurrentClock = mylablib::CMLTools::GetCurrentClock();
						if (m_bDisplayRawFormat)
						{
							m_OutputFile << nDataIndex << L','<< dSSQ << L',' << nCurrentClock - nStartClock << L"\n";
						}
						else
						{
							m_OutputFile << nDataIndex << L": SSQ : "<< dSSQ << L'(' << horizonDataSet.Count() << L")(" << vCenters.size() << L") ";
							m_OutputFile << nDataIndex << L": Time: " << nCurrentClock - nStartClock << L"\n";
						}
					}
					else
					{

						nStatCount++;
						if (nStatCount == m_nStatLength)
						{
							dAvgSSQ /= (double)m_nStatLength;

							// Output the Average SSQ
							nCurrentClock = mylablib::CMLTools::GetCurrentClock();
							if (m_bDisplayRawFormat)
							{
								m_OutputFile << nDataIndex << L','<< dAvgSSQ << L',' << nCurrentClock - nStartClock << L"\n";
							}
							else
							{
								m_OutputFile << nDataIndex << L": AverageSSQ : "<< dAvgSSQ << L" Time: " << nCurrentClock - nStartClock << L"\n";
							}
							std::wcout<< nDataIndex << L": AverageSSQ : "<< dAvgSSQ << L" Time: " << nCurrentClock - nStartClock << L"\n";

							nStatCount = 0;
							dAvgSSQ = 0.0;
						}
					}
				}

				// Save the snapshot
				m_CluStream.SaveSnapshot(preSnap);
			}
		}
		
		// Reload the data points from data stream
		nRet = m_StreamLoader.Load(dataSet, m_nStreamBufferSize);
		if (ML_FAILED(nRet))
		{
			m_OutputFile.close();
			m_StreamLoader.Close();
			_ASSERT(false);
			return nRet;
		}
	}

	return 0;
}


int CMLConsoleCluStream::InitCluStream(void)
{
	// Initialize the CluStream
	m_CluStream.SetInitNumber(m_nInitNumber);
	m_CluStream.SetInitNumMicroClusters(m_nInitNumMicroClusters);
	m_CluStream.SetMaxNumMicroClusters(m_nMaxNumMicroClusters);
	m_CluStream.SetMaxMicroClusterRadius(m_dMaxMicroClusterRadius);
	m_CluStream.SetMaxTimeStampBelow(m_dMaxTimeStampBelow);
	m_CluStream.SetNumLastTimeCheck(m_nNumLastTimeCheck);

	return 0;
}

int CMLConsoleCluStream::InitStreamLoader(void)
{
	// Load the input dataset
	int nRet = m_StreamLoader.Initialize(m_wstrInputFileName, 0, m_nStreamMaxLength);
	if (ML_FAILED(nRet))
	{
		CMLConsolePrinter::PrintError(L"Cannot open the input dataset file : %s\n", m_wstrInputFileName.c_str());
		_ASSERT(false);
		return nRet;
	}
	return 0;
}

int CMLConsoleCluStream::InitOutputFile(void)
{
	m_OutputFile.open(m_wstrOutputFileName.c_str());
	if (m_OutputFile.fail())
	{
		_ASSERT(false);
		return -1;
	}
	return 0;
}

int CMLConsoleCluStream::CloseAll(void)
{
	m_StreamLoader.Close();
	m_OutputFile.close();
	return 0;
}

int CMLConsoleCluStream::OutputMacroClusters(const mylablib::CMLDataSet& dataSet, const std::vector<mylablib::CMLDataPoint>& vCenters)
{
	double dSSQ = mylablib::CMLTools_Stat::CalSSQ(dataSet, vCenters, m_CluStream.GetDistanceFunc());
	m_OutputFile << L"SSQ : "<< dSSQ << L'(' << dataSet.Count() << L")(" << vCenters.size() << L")\n";
	return 0;
}

int CMLConsoleCluStream::OutputMicroClusters(void)
{
	const std::vector<CMLCluStreamMicroCluster*> vpMicroClusters = m_CluStream.GetMicroClusters();
	int i;
	for (i=0; i<(int)vpMicroClusters.size(); i++)
	{
		const CMLCluStreamMicroCluster* pMicroCluster = vpMicroClusters[i];
		m_OutputFile << pMicroCluster->GetCenter().ToWString();
		m_OutputFile << L'(' << pMicroCluster->GetDataPointCount() << L')';
		if(i < (int)vpMicroClusters.size() -1)
			m_OutputFile << L" : ";
	}
	m_OutputFile<<L'\n';
	return 0;
}
